package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

// 获取当前工作目录
func getPwd() string {
	dir, err := os.Getwd()
	if err != nil {
		fmt.Println("获取工作目录出错：", err)
		return ""
	} else {
		return dir
	}
}

func existDir(dir string) bool {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		return false
	} else {
		return true
	}
}
func createDir(dir string) bool {
	err := os.Mkdir(dir, 0755)
	if err != nil {
		fmt.Println("创建目录失败:", err)
		return false
	} else {
		return true
	}
}
func checkDir() {
	all := [...]string{"video", "temp"}
	for _, value := range all {
		exist := existDir(value)
		if !exist {
			dir := getPwd()
			dir = dir + "/" + value
			createDir(dir)
		}
	}
}
func curl(url string) string {
	client := &http.Client{
		Timeout: time.Second * 10, // 设置超时时间为 10 秒钟
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Fatal("Error creating request: ", err)
		return ""
	}
	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("Error send request", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		log.Fatal("StatusCode: ", resp.StatusCode)
		return ""
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("Error body: ", err)
		return ""
	}
	if resp.StatusCode != 200 {
		log.Fatal("StatusCode: ", resp.StatusCode)
		return ""
	}
	return string(body)
}
func getTsName(url string) []string {
	res := curl(url)
	text := strings.Split(res, "\n")
	if len(text) < 0 {
		fmt.Println("get m3u8 file error")
	} else {
		if text[0] != "#EXTM3U" {
			fmt.Println("not m3u8 data")
		}
	}
	var tsName []string
	var tsTime []string
	for _, value := range text {
		if strings.Contains(value, ".ts") {
			tsName = append(tsName, value)
		} else if strings.Contains(value, "#EXTINF:") {
			tsTime = append(tsTime, value)
		}
	}
	if len(tsName) != len(tsTime) {
		fmt.Println("m3u8 is ts file and time err")
	}
	return tsName
}
func downloadFile(url string, wg *sync.WaitGroup) {
	defer wg.Done()
	// 发起HTTP GET请求
	response, err := http.Get(url)
	if err != nil {
		fmt.Printf("下载 %s 失败: %s\n", url, err)
		return
	}
	defer response.Body.Close()
	// 创建本地文件
	file, err := os.Create(getFileName(url))
	if err != nil {
		fmt.Printf("创建文件失败: %s\n", err)
		return
	}
	defer file.Close()
	// 将HTTP响应体写入本地文件
	_, err = io.Copy(file, response.Body)
	if err != nil {
		fmt.Printf("写入文件失败: %s\n", err)
		return
	}
	fmt.Printf("%s 下载完成\n", url) // 这里加个百分比进度条
}
func getFileName(url string) string {
	parts := strings.Split(url, "/")
	if len(parts) > 0 {
		dir := getPwd()
		file := dir + "/temp/" + parts[len(parts)-1]
		return file
	}
	return ""
}
func downTs(tsFiles []string, link string) {
	baseUrl := strings.Replace(link, "index.m3u8", "", -1)
	fmt.Println("共计多少99个切片，准备开始下载")
	//使用WaitGroup来等待所有协程完成
	var wg sync.WaitGroup
	//启动多个协程并发下载ts切片
	for _, tsFile := range tsFiles {
		url := baseUrl + tsFile
		wg.Add(1)
		go downloadFile(url, &wg)
	}
	// 等待所有协程完成
	wg.Wait()
	fmt.Println("所有切片下载完成")
	//开始合并切片
	mergeTs(tsFiles)
}
func AssemblyData(tsName []string) []string {
	fmt.Println("开始预处理数据")
	var files []string
	dir := getPwd()
	for _, name := range tsName {
		tmp := dir + "/temp/" + name
		fileDir := filepath.Join(tmp)
		files = append(files, fileDir)
	}
	return files
}
func mergeTs(tsName []string) {
	// 检测操作系统是否支持合并命令
	checkCommand()
	// 预处理数据
	files := AssemblyData(tsName)
	// 开始合并
	var cmd *exec.Cmd
	var cmdStr string
	// 定义储存文件名
	dir := getPwd()
	currentTime := time.Now()
	layout := "20060102150405" // 时间戳格式
	filename := dir + "/video/" + currentTime.Format(layout) + ".ts"
	OS := readOS()
	// 选择合并命令
	if OS == "windows" {
		filepathCat := strings.Join(files, " + ")
		cmdStr = "copy /b " + filepathCat + " " + filename
		cmd = exec.Command("cmd", "/c", cmdStr)
	} else if OS == "linux" {
		filepathCat := strings.Join(files, " ")
		cmdStr = "cat " + filepathCat + " > " + filename
		cmd = exec.Command("sh", "-c", cmdStr)
	} else if OS == "MacOS" {
		filepathCat := strings.Join(files, " ")
		cmdStr = "cat " + filepathCat + " > " + filename
		cmd = exec.Command("sh", "-c", cmdStr)
	}
	fmt.Println("开始合并文件,储存路径为:" + filename)
	err := cmd.Run()
	if err != nil {
		fmt.Println("执行命令失败:", err)
		return
	}
	fmt.Println("合并完成")
	//看是否能带出 合并文件百分比进度
}
func checkCommand() string {
	OS := readOS()
	if OS == "windows" {
		cmd := exec.Command("copy", "/?")
		err := cmd.Run()
		if err != nil {
			fmt.Println("当前操作系统"+OS+",不支持copy命令,请检查系统环境", err)
		}
	} else if OS == "linux" {
		cmd := exec.Command("sh", "-c", "echo Hello")
		err := cmd.Run()
		if err != nil {
			fmt.Println("当前操作系统" + OS + ",不支持sh命令,请检查系统环境")
		}
	} else if OS == "MacOS" {
		cmd := exec.Command("sh", "-c", "echo Hello")
		err := cmd.Run()
		if err != nil {
			fmt.Println("当前操作系统" + OS + ",不支持sh命令,请检查系统环境")
		}
	}
	return ""
}
func readOS() string {
	switch runtime.GOOS {
	case "windows":
		return "windows"
	case "linux":
		return "linux"
	case "darwin":
		return "MacOS"
	default:
		return "Unknown"
	}
}
func runMethodCheck() {
	if len(os.Args) == 1 {
		// 判断是否命令行执行的
		//		如果是win 提示使用方法
		//		如果是linux 提示使用方法
		// 判断双击执行的
		//		如果是win 则打开下载窗口
		//		如果是linux 则打开下载窗口

	} else if len(os.Args) == 2 {
		link := os.Args[1]
		fmt.Println(link)
		// 获取ts_name
		tsFiles := getTsName(link)
		downTs(tsFiles, link)
		//判断是链接 包含 https、http，m3u8
		//如果不是，判断是否txt
		//是txt，拆分多条链接，循环下载
		//不是txt，提示正确链接或txt
	}

}
func main() {
	checkDir()
	runMethodCheck()

	//请输入保存文件名,无文件名,则默认时间命名.只支持前缀
	//支持命令+1.txt方式 单链接+多链接 下载
	//支持运行后,粘贴单条链接 下载
	//增加 对比ts文件大小一致，则不下载
	//本地ts大小为0文件，才开始下载
	//检测文件已下载数量，和计划下载数量是否一致，如果不是，则重试
	//重试全部大于5次，则强制合并并清空临时文件夹
	//合并完成则清空temp文件夹
	//foreach
	//for key, value := range all {}
}
